package org.timemachine.generator;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;

import javax.persistence.JoinTable;

import org.timemachine.classloader.TimeMachineClassLoader;
import org.timemachine.classloader.factory.ClassLoaderFactory;
import org.timemachine.factory.ClassPoolFactory;
import org.timemachine.generator.spi.AnnoChecker;
import org.timemachine.generator.spi.ChangeClassGenerator;
import org.timemachine.generator.spi.Persistency;
import org.timemachine.tool.ClassHelperImpl;
import org.timemachine.tool.spi.ClassHelper;
import org.timemachine.util.AnnoUtil;
import org.timemachine.util.FileUtil;

public class TimeMachineGenerator {
	
	public static void main(String[] args) {
		
		String workingDir = System.getProperty("user.dir");
		String pathCreateFolder = workingDir + "\\temp";
		String replacementStr = pathCreateFolder.replace("\\", "/");
		String jarName = "timeMachineChangeClasses.jar";
		
		try {
			
			File file = FileUtil.getFileByProjectPath(workingDir);
			
			TimeMachineClassLoader cloader = ClassLoaderFactory.getTimeMachineClassLoader(file);
            cloader.loadClasses();
            
            ClassPool classPool = ClassPoolFactory.getDefaultClassPool(cloader);

			ClassHelper classHelper = new ClassHelperImpl();
			Set<Class<?>> clazzesWithAnnotations = classHelper.findClassesWithAnnotation(cloader.getClasses(), AnnoUtil.getDefinedAnnos());
			Set<String> clazzNamesChange = new HashSet<String>();
			
			ChangeClassGenerator changeClassGeneratorImpl = new ChangeClassGeneratorImpl();
			AnnoChecker annoChecker = new AnnoCheckerImpl();
			JoinTable annoJoinTable = null;
			for (Class<?> class1 : clazzesWithAnnotations) {
				
				CtClass cc = changeClassGeneratorImpl.generateChangeClass(classPool, class1);
				changeClassGeneratorImpl.addPrimaryKeyMethodsToClass(cc);
				
				CtField[] fields = cc.getDeclaredFields();
				for (CtField ctField : fields) {
					annoChecker.checkIdAnnotation(cc, ctField);
					annoChecker.checkOneToManyAnnotation(cc, ctField);
					annoJoinTable = annoChecker.checkManyToManyAnnotation(cc, ctField);
					annoChecker.checkOneToOneAnnotation(cc, ctField);
					
					if (annoJoinTable != null) {
						CtClass joinClass = changeClassGeneratorImpl.createChangeClass(classPool, cc.getPackageName(), annoJoinTable.name());
						changeClassGeneratorImpl.addMasterAndChildFieldsToClass(class1, cc, ctField, joinClass);						
						changeClassGeneratorImpl.writeFile(joinClass, pathCreateFolder);
					}
					
				}
				changeClassGeneratorImpl.addChangeIdField(cc);
				String changeName = changeClassGeneratorImpl.writeFile(cc, pathCreateFolder);
				clazzNamesChange.add(changeName);
			}
			
			Persistency persistency = new PersistencyImpl();
			File persistenceFile  = persistency.findPersistence(workingDir);
			persistency.injectChangeJar(persistenceFile);
			persistency.injectChangeClasses(persistenceFile, clazzNamesChange);

		} catch (Exception e) {
			System.out.println("Hata aldik");
			e.printStackTrace();
		}
		
		try {
			String libDirectory = workingDir + "\\WebContent\\WEB-INF\\lib\\";
			
			Manifest manifest = new Manifest();
			manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION,"1.0");
			JarOutputStream target = new JarOutputStream(new FileOutputStream(libDirectory + jarName), manifest);
			List<File> sourceList = Arrays.asList(new File(pathCreateFolder).listFiles());
			
			for (File _file : sourceList) {
				generateJar(_file, target, replacementStr);
			}
			
			target.close();
			
		} catch (Exception e) {
			System.err.println(e.toString());
		}
		
		File directory = new File(pathCreateFolder);
    	if(!directory.exists()){
           System.out.println("Directory does not exist.");
           System.exit(0);
        }else{
           try{
               deleteDirectory(directory);
           }catch(IOException e){
               e.printStackTrace();
               System.exit(0);
           }
        }
		
    	System.out.println("Change generation is COMPLETED!");
	}
	
	
	private static void generateJar(File source, JarOutputStream target, String replacementStr) throws IOException {
		BufferedInputStream in = null;
		try {
			if (source.isDirectory()) {
				String name = source.getPath().replace("\\", "/");
				if (!name.isEmpty()) {
					if (!name.endsWith("/"))
						name += "/";
					if(name.startsWith(replacementStr)){
						name = name.replaceAll(replacementStr+"/", "");
					}
					JarEntry entry = new JarEntry(name);
					entry.setTime(source.lastModified());
					target.putNextEntry(entry);
					target.closeEntry();
				}
				for (File nestedFile : source.listFiles())
					generateJar(nestedFile, target, replacementStr);
				return;
			}
			JarEntry entry = new JarEntry((source.getPath().replace("\\", "/")).replaceAll(replacementStr+"/", ""));
			entry.setTime(source.lastModified());
			target.putNextEntry(entry);
			in = new BufferedInputStream(new FileInputStream(source));

			byte[] buffer = new byte[1024];
			while (true) {
				int count = in.read(buffer);
				if (count == -1)
					break;
				target.write(buffer, 0, count);
			}
			target.closeEntry();
		} finally {
			if (in != null)
				in.close();
		}
	}
	
	public static void deleteDirectory(File file) throws IOException {

		if (file.isDirectory()) {
			if (file.list().length == 0) {
				file.delete();
				System.out.println("Directory is deleted : " + file.getAbsolutePath());
			} else {
				String files[] = file.list();
				for (String temp : files) {
					File fileDelete = new File(file, temp);
					deleteDirectory(fileDelete);
				}
				if (file.list().length == 0) {
					file.delete();
					System.out.println("Directory is deleted : " + file.getAbsolutePath());
				}
			}

		} else {
			file.delete();
			System.out.println("File is deleted : " + file.getAbsolutePath());
		}
	} 	
}
